// HSS.Forms.CompareManager.cs
// ----------------------------------------------------------------------------
// Licensed under the MIT license
// http://www.opensource.org/licenses/mit-license.html
// ----------------------------------------------------------------------------
// HighSpeed-Solutions, LLC
// Copyright (c) 2001-2010
// ----------------------------------------------------------------------------
// File:       CompareManager.cs
// Author:     HSS\gbanta
// Created:    08/12/2010
// Modified:   12/04/2010
// ----------------------------------------------------------------------------
namespace HSS.Forms
{
	#region Using Directives
	using System;
	using System.Collections;
	using System.Collections.Generic;
	#endregion

	#region ComparerManager
	/// <summary>
	/// The ComparerManager can map Types to IComparers that will correctly
	/// compare objects of that type
	/// </summary>
	/// <remarks>
	/// <para>Users can add their own comparers for use with their own object types,
	/// using the Register() method.</para>
	/// 
	/// <para>
	/// Types registered by default...
	/// <code>
	/// Register(typeof(char), new CharValueComparer());
	/// Register(typeof(Char), new CharValueComparer());
	/// Register(typeof(short), new ShortValueComparer());
	/// Register(typeof(Int16), new ShortValueComparer());
	/// Register(typeof(ushort), new UShortValueComparer());
	/// Register(typeof(UInt16), new UShortValueComparer());
	/// Register(typeof(int), new IntValueComparer());
	/// Register(typeof(Int32), new IntValueComparer());
	/// Register(typeof(uint), new UIntValueComparer());
	/// Register(typeof(UInt32), new UIntValueComparer());
	/// Register(typeof(long), new LongValueComparer());
	/// Register(typeof(Int64), new LongValueComparer());
	/// Register(typeof(ulong), new ULongValueComparer());
	/// Register(typeof(UInt64), new ULongValueComparer());
	/// Register(typeof(string), new StringValueComparer());
	/// Register(typeof(double), new DoubleValueComparer());
	/// Register(typeof(Double), new DoubleValueComparer());
	/// Register(typeof(float), new FloatValueComparer());
	/// Register(typeof(Single), new FloatValueComparer());
	/// Register(typeof(DateTime), new DateValueComparer());
	/// Register(typeof(TimeSpan), new TimeSpanValueComparer());
	/// </code>
	/// </para>
	/// </remarks>
	public static class ComparerManager
	{
		/// <summary>
		/// Setup the static information about the ComparerManager, and
		/// populate the comparer's for all basic types.
		/// </summary>
		static ComparerManager()
		{
			dict = new Dictionary<Type, IComparer>();

			Register(typeof(char), new CharValueComparer());
			Register(typeof(Char), new CharValueComparer());
			Register(typeof(short), new ShortValueComparer());
			Register(typeof(Int16), new ShortValueComparer());
			Register(typeof(ushort), new UShortValueComparer());
			Register(typeof(UInt16), new UShortValueComparer());
			Register(typeof(int), new IntValueComparer());
			Register(typeof(Int32), new IntValueComparer());
			Register(typeof(uint), new UIntValueComparer());
			Register(typeof(UInt32), new UIntValueComparer());
			Register(typeof(long), new LongValueComparer());
			Register(typeof(Int64), new LongValueComparer());
			Register(typeof(ulong), new ULongValueComparer());
			Register(typeof(UInt64), new ULongValueComparer());
			Register(typeof(string), new StringValueComparer());
			Register(typeof(double), new DoubleValueComparer());
			Register(typeof(Double), new DoubleValueComparer());
			Register(typeof(float), new FloatValueComparer());
			Register(typeof(Single), new FloatValueComparer());
			Register(typeof(DateTime), new DateValueComparer());
			Register(typeof(TimeSpan), new TimeSpanValueComparer());
		}

		/// <summary>
		/// Register the given comparer to be used to compare objects of the given type
		/// </summary>
		/// <param name="t">The type to register</param>
		/// <param name="comparer">The implemented comparer for the type</param>
		public static void Register(Type t, IComparer comparer)
		{
			dict[t] = comparer;
		}

		/// <summary>
		/// Return an IComparer that will correctly compare two objects like the given one
		/// </summary>
		/// <param name="anObject">The object to get the comparer for</param>
		/// <returns>One of the registered comparers based on the object if found otherwise null</returns>
		public static IComparer GetComparerFor(object anObject)
		{
			if (anObject == null)
				return null;
			else
				return GetComparerFor(anObject.GetType());
		}

		/// <summary>
		/// Return an IComparer that will correctly compare two objects of the given type
		/// </summary>
		/// <param name="t">The type used to look up a registered comparer</param>
		/// <returns>The comparer for the given type, otherwise null</returns>
		public static IComparer GetComparerFor(Type t)
		{
			IComparer comparer;

			if (dict.TryGetValue(t, out comparer))
				return comparer;
			else
				return null;
		}

		static private Dictionary<Type, IComparer> dict;

		#region Compare Classes

		/// <summary>
		/// Compare two strings using natural numerical ordering
		/// </summary>	
		class StringValueComparer : StringComparer
		{
			#region Fields
			private StringComparison comparison = StringComparison.CurrentCulture;
			#endregion

			#region Constructors
			public StringValueComparer()
			{
			}
			public StringValueComparer(StringComparison comparison)
			{
				this.comparison = comparison;
			}
			#endregion

			#region Overrides
			public override bool Equals(string x, string y)
			{
				return x.Equals(y);
			}
			public override int GetHashCode(string obj)
			{
				return obj.GetHashCode();
			}
			#endregion

			#region Methods

			/// <summary>
			/// Compares two strings using natural numerical ordering
			/// </summary>
			/// <param name="one">The first string</param>
			/// <param name="two">The second string</param>
			/// <returns>A signed number indicating the relative values of this instance and the value parameter. </returns>
			/// <list type="table">
			///   <listheader>
			///      <term>Return Value</term>
			///      <description>Description</description>
			///   </listheader>
			///   <item>
			///      <term>Less than zero</term>
			///      <description>The first string is less than the second string</description>
			///   </item>
			///   <item>
			///      <term>Zero</term>
			///      <description>The two strings are equal</description>
			///   </item>
			///   <item>
			///      <term>Greater than zero</term>
			///      <description>The first string is greater than the second string</description>
			///   </item>
			/// </list>
			public override int Compare(string one, string two)
			{
				int retVal = 0;
				if (!String.IsNullOrEmpty(one) && !String.IsNullOrEmpty(two))
				{
					int c1 = 0, c2 = 0;
					int len1 = one.Length, len2 = two.Length;
					while (len1 > c1)
					{
						if (!(len2 > c2)) { retVal = 1; break; }

						// IsDigit determines if a Char is a radix-10 digit. (only DecimalDigitNumber)
						// IsNumber determines if a Char is of any numeric Unicode category. (including LetterNumber, or OtherNumber)
						if (char.IsDigit(one[c1]))
						{
							if (!(char.IsDigit(two[c2]))) { retVal = -1; break; }
							// TakeNumber is also aware of unicode numbers
							retVal = TakeNumber(one, ref c1).CompareTo(TakeNumber(two, ref c2));
							if (retVal != 0)
								break;
						}
						else if (char.IsDigit(two[c2]))
						{
							retVal = 1;
							break;
						}
						else
						{
							// Use the locale-aware string compare...
							retVal = String.Compare(one, c1, two, c2, 1, comparison);
							if (retVal != 0)
								break;
							++c1;
							++c2;
						}
					}
					if (0 == retVal && len2 > c2) { retVal = -1; };
				}
				return retVal;
			}

			/// <summary>
			/// Parse a number of indeterminate length from a string
			/// </summary>
			/// <param name="numerical">A string with a number in it</param>
			/// <param name="index">The index to start parsing at (is set to the index of the first non-numerical character - might be beyond the length of the string)</param>
			/// <returns>The number parsed from the string</returns>
			private static int TakeNumber(string numerical, ref int index)
			{
				//// this test is only needed if it's possible to call the method incorrectly
				//if( !char.IsNumber( numerical[index] ) ) {
				//   throw new InvalidOperationException( "Character at index " + index + " of '" + numerical + "' is not numerical" );
				//}

				// make a copy of the starting point
				int start = index;
				while (++index < numerical.Length && char.IsDigit(numerical[index])) ;
				// If provider is null, the NumberFormatInfo for the current culture is used.
				return int.Parse(numerical.Substring(start, index - start), null);
			}

			#endregion
		}

		/// <summary>
		/// Compare two strings, case sensitive
		/// </summary>
		class CaseSensitiveStringValueComparer : IComparer
		{
			public int Compare(object x, object y)
			{
				return String.Compare((string)x, (string)y, false);
			}
		}

		/// <summary>
		/// Compare two characters
		/// </summary>
		class CharValueComparer : IComparer
		{
			public int Compare(object x, object y)
			{
				return ((char)x).CompareTo((char)y);
			}
		}

		/// <summary>
		/// Compare two shorts
		/// </summary>
		class ShortValueComparer : IComparer
		{
			public int Compare(object x, object y)
			{
				return ((short)x) - ((short)y);
			}
		}

		/// <summary>
		/// Compare two unsigned shorts
		/// </summary>
		class UShortValueComparer : IComparer
		{
			public int Compare(object x, object y)
			{
				return ((ushort)x).CompareTo((ushort)y);
			}
		}

		/// <summary>
		/// Compare two integers
		/// </summary>
		class IntValueComparer : IComparer
		{
			public int Compare(object x, object y)
			{
				return ((int)x) - ((int)y);
			}
		}

		/// <summary>
		/// Compare two unsigned integers
		/// </summary>
		class UIntValueComparer : IComparer
		{
			public int Compare(object x, object y)
			{
				return ((uint)x).CompareTo((uint)y);
			}
		}

		/// <summary>
		/// Compare two longs
		/// </summary>
		class LongValueComparer : IComparer
		{
			public int Compare(object x, object y)
			{
				return ((long)x).CompareTo((long)y);
			}
		}

		/// <summary>
		/// Compare two unsigned longs
		/// </summary>
		class ULongValueComparer : IComparer
		{
			public int Compare(object x, object y)
			{
				return ((ulong)x).CompareTo((ulong)y);
			}
		}

		/// <summary>
		/// Compare two floats
		/// </summary>
		class FloatValueComparer : IComparer
		{
			public int Compare(object x, object y)
			{
				return ((float)x).CompareTo((float)y);
			}
		}

		/// <summary>
		/// Compare two doubles
		/// </summary>
		class DoubleValueComparer : IComparer
		{
			public int Compare(object x, object y)
			{
				return ((double)x).CompareTo((double)y);
			}
		}

		/// <summary>
		/// Compare two decimals
		/// </summary>
		class DecimalValueComparer : IComparer
		{
			public int Compare(object x, object y)
			{
				return ((decimal)x).CompareTo((decimal)y);
			}
		}

		/// <summary>
		/// Compare two dates
		/// </summary>
		class DateValueComparer : IComparer
		{
			public int Compare(object x, object y)
			{
				return ((DateTime)x).CompareTo((DateTime)y);
			}
		}

		/// <summary>
		/// Compare two time spans
		/// </summary>
		class TimeSpanValueComparer : IComparer
		{
			public int Compare(object x, object y)
			{
				return ((TimeSpan)x).CompareTo((TimeSpan)y);
			}
		}

		#endregion

	}
	#endregion
}